/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.

This file is part of XreaL source code.

XreaL source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

XreaL source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with XreaL source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/
// cvar.c -- dynamic variable tracking

#include "q_shared.h"
#include "qcommon.h"

cvar_t         *cvar_vars = NULL;
cvar_t         *cvar_cheats;
int             cvar_modifiedFlags;

#define	MAX_CVARS	1024
cvar_t          cvar_indexes[MAX_CVARS];
int             cvar_numIndexes;

#define FILE_HASH_SIZE		256
static cvar_t  *hashTable[FILE_HASH_SIZE];

cvar_t         *Cvar_Set2(const char *var_name, const char *value, qboolean force);

/*
================
return a hash value for the filename
================
*/
static long generateHashValue(const char *fname)
{
	int             i;
	long            hash;
	char            letter;

	hash = 0;
	i = 0;
	while(fname[i] != '\0')
	{
		letter = tolower(fname[i]);
		hash += (long)(letter) * (i + 119);
		i++;
	}
	hash &= (FILE_HASH_SIZE - 1);
	return hash;
}

/*
============
Cvar_ValidateString
============
*/
static qboolean Cvar_ValidateString(const char *s)
{
	if(!s)
	{
		return qfalse;
	}
	if(strchr(s, '\\'))
	{
		return qfalse;
	}
	if(strchr(s, '\"'))
	{
		return qfalse;
	}
	if(strchr(s, ';'))
	{
		return qfalse;
	}
	return qtrue;
}

/*
============
Cvar_FindVar
============
*/
static cvar_t  *Cvar_FindVar(const char *var_name)
{
	cvar_t         *var;
	long            hash;

	hash = generateHashValue(var_name);

	for(var = hashTable[hash]; var; var = var->hashNext)
	{
		if(!Q_stricmp(var_name, var->name))
		{
			return var;
		}
	}

	return NULL;
}

/*
============
Cvar_VariableValue
============
*/
float Cvar_VariableValue(const char *var_name)
{
	cvar_t         *var;

	var = Cvar_FindVar(var_name);
	if(!var)
		return 0;
	return var->value;
}


/*
============
Cvar_VariableIntegerValue
============
*/
int Cvar_VariableIntegerValue(const char *var_name)
{
	cvar_t         *var;

	var = Cvar_FindVar(var_name);
	if(!var)
		return 0;
	return var->integer;
}


/*
============
Cvar_VariableString
============
*/
char           *Cvar_VariableString(const char *var_name)
{
	cvar_t         *var;

	var = Cvar_FindVar(var_name);
	if(!var)
		return "";
	return var->string;
}


/*
============
Cvar_VariableStringBuffer
============
*/
void Cvar_VariableStringBuffer(const char *var_name, char *buffer, int bufsize)
{
	cvar_t         *var;

	var = Cvar_FindVar(var_name);
	if(!var)
	{
		*buffer = 0;
	}
	else
	{
		Q_strncpyz(buffer, var->string, bufsize);
	}
}

/*
============
Cvar_Flags
============
*/
int Cvar_Flags(const char *var_name)
{
	cvar_t         *var;

	if(!(var = Cvar_FindVar(var_name)))
		return CVAR_NONEXISTENT;
	else
		return var->flags;
}

/*
============
Cvar_CommandCompletion
============
*/
void Cvar_CommandCompletion(void (*callback) (const char *s))
{
	cvar_t         *cvar;

	for(cvar = cvar_vars; cvar; cvar = cvar->next)
	{
		if(cvar->name)
			callback(cvar->name);
	}
}

/*
============
Cvar_Validate
============
*/
static const char *Cvar_Validate(cvar_t * var, const char *value, qboolean warn)
{
	static char     s[MAX_CVAR_VALUE_STRING];
	float           valuef;
	qboolean        changed = qfalse;

	if(!var->validate)
		return value;

	if(!value)
		return value;

	if(Q_isanumber(value))
	{
		valuef = atof(value);

		if(var->integral)
		{
			if(!Q_isintegral(valuef))
			{
				if(warn)
					Com_Printf("WARNING: cvar '%s' must be integral", var->name);

				valuef = (int)valuef;
				changed = qtrue;
			}
		}
	}
	else
	{
		if(warn)
			Com_Printf("WARNING: cvar '%s' must be numeric", var->name);

		valuef = atof(var->resetString);
		changed = qtrue;
	}

	if(valuef < var->min)
	{
		if(warn)
		{
			if(changed)
				Com_Printf(" and is");
			else
				Com_Printf("WARNING: cvar '%s'", var->name);

			if(Q_isintegral(var->min))
				Com_Printf(" out of range (min %d)", (int)var->min);
			else
				Com_Printf(" out of range (min %f)", var->min);
		}

		valuef = var->min;
		changed = qtrue;
	}
	else if(valuef > var->max)
	{
		if(warn)
		{
			if(changed)
				Com_Printf(" and is");
			else
				Com_Printf("WARNING: cvar '%s'", var->name);

			if(Q_isintegral(var->max))
				Com_Printf(" out of range (max %d)", (int)var->max);
			else
				Com_Printf(" out of range (max %f)", var->max);
		}

		valuef = var->max;
		changed = qtrue;
	}

	if(changed)
	{
		if(Q_isintegral(valuef))
		{
			Com_sprintf(s, sizeof(s), "%d", (int)valuef);

			if(warn)
				Com_Printf(", setting to %d\n", (int)valuef);
		}
		else
		{
			Com_sprintf(s, sizeof(s), "%f", valuef);

			if(warn)
				Com_Printf(", setting to %f\n", valuef);
		}

		return s;
	}
	else
		return value;
}


/*
============
Cvar_Get

If the variable already exists, the value will not be set unless CVAR_ROM
The flags will be or'ed in if the variable exists.
============
*/
cvar_t         *Cvar_Get(const char *var_name, const char *var_value, int flags)
{
	cvar_t         *var;
	long            hash;
	int             index;

	if(!var_name || !var_value)
	{
		Com_Error(ERR_FATAL, "Cvar_Get: NULL parameter");
	}

	if(!Cvar_ValidateString(var_name))
	{
		Com_Printf("invalid cvar name string: %s\n", var_name);
		var_name = "BADNAME";
	}

#if 0							// FIXME: values with backslash happen
	if(!Cvar_ValidateString(var_value))
	{
		Com_Printf("invalid cvar value string: %s\n", var_value);
		var_value = "BADVALUE";
	}
#endif

	var = Cvar_FindVar(var_name);

	if(var)
	{
		var_value = Cvar_Validate(var, var_value, qfalse);

		// if the C code is now specifying a variable that the user already
		// set a value for, take the new value as the reset value
		if(var->flags & CVAR_USER_CREATED)
		{
			var->flags &= ~CVAR_USER_CREATED;
			Z_Free(var->resetString);
			var->resetString = CopyString(var_value);

			if(flags & CVAR_ROM)
			{
				// this variable was set by the user,
				// so force it to value given by the engine.

				if(var->latchedString)
					Z_Free(var->latchedString);

				var->latchedString = CopyString(var_value);
			}
		}

		// Make sure the game code cannot mark engine-added variables as gamecode vars
		if(var->flags & CVAR_VM_CREATED)
		{
			if(!(flags & CVAR_VM_CREATED))
				var->flags &= ~CVAR_VM_CREATED;
		}
		else
		{
			if(flags & CVAR_VM_CREATED)
				flags &= ~CVAR_VM_CREATED;
		}

		var->flags |= flags;

		// only allow one non-empty reset string without a warning
		if(!var->resetString[0])
		{
			// we don't have a reset string yet
			Z_Free(var->resetString);
			var->resetString = CopyString(var_value);
		}
		else if(var_value[0] && strcmp(var->resetString, var_value))
		{
			Com_DPrintf("Warning: cvar \"%s\" given initial values: \"%s\" and \"%s\"\n", var_name, var->resetString, var_value);
		}
		// if we have a latched string, take that value now
		if(var->latchedString)
		{
			char           *s;

			s = var->latchedString;
			var->latchedString = NULL;	// otherwise cvar_set2 would free it
			Cvar_Set2(var_name, s, qtrue);
			Z_Free(s);
		}

		// ZOID--needs to be set so that cvars the game sets as 
		// SERVERINFO get sent to clients
		cvar_modifiedFlags |= flags;

		return var;
	}

	//
	// allocate a new cvar
	//

	// find a free cvar
	for(index = 0; index < MAX_CVARS; index++)
	{
		if(!cvar_indexes[index].name)
			break;
	}

	if(index >= MAX_CVARS)
	{
		if(!com_errorEntered)
			Com_Error(ERR_FATAL, "Error: Too many cvars, cannot create a new one!");

		return NULL;
	}

	var = &cvar_indexes[index];

	if(index >= cvar_numIndexes)
		cvar_numIndexes = index + 1;

	var->name = CopyString(var_name);
	var->string = CopyString(var_value);
	var->modified = qtrue;
	var->modificationCount = 1;
	var->value = atof(var->string);
	var->integer = atoi(var->string);
	var->resetString = CopyString(var_value);
	var->validate = qfalse;

	// link the variable in
	var->next = cvar_vars;
	if(cvar_vars)
		cvar_vars->prev = var;

	var->prev = NULL;
	cvar_vars = var;

	var->flags = flags;
	// note what types of cvars have been modified (userinfo, archive, serverinfo, systeminfo)
	cvar_modifiedFlags |= var->flags;

	hash = generateHashValue(var_name);
	var->hashIndex = hash;

	var->hashNext = hashTable[hash];
	if(hashTable[hash])
		hashTable[hash]->hashPrev = var;

	var->hashPrev = NULL;
	hashTable[hash] = var;

	return var;
}

/*
============
Cvar_Print

Prints the value, default, and latched string of the given variable
============
*/
void Cvar_Print(cvar_t * v)
{
	Com_Printf("\"%s\" is:\"%s" S_COLOR_WHITE "\"", v->name, v->string);

	if(!(v->flags & CVAR_ROM))
	{
		if(!Q_stricmp(v->string, v->resetString))
		{
			Com_Printf(", the default");
		}
		else
		{
			Com_Printf(" default:\"%s" S_COLOR_WHITE "\"", v->resetString);
		}
	}

	Com_Printf("\n");

	if(v->latchedString)
	{
		Com_Printf("latched: \"%s\"\n", v->latchedString);
	}
}

/*
============
Cvar_Set2
============
*/
cvar_t         *Cvar_Set2(const char *var_name, const char *value, qboolean force)
{
	cvar_t         *var;

//  Com_DPrintf( "Cvar_Set2: %s %s\n", var_name, value );

	if(!Cvar_ValidateString(var_name))
	{
		Com_Printf("invalid cvar name string: %s\n", var_name);
		var_name = "BADNAME";
	}

#if 0							// FIXME
	if(value && !Cvar_ValidateString(value))
	{
		Com_Printf("invalid cvar value string: %s\n", value);
		var_value = "BADVALUE";
	}
#endif

	var = Cvar_FindVar(var_name);
	if(!var)
	{
		if(!value)
		{
			return NULL;
		}
		// create it
		if(!force)
		{
			return Cvar_Get(var_name, value, CVAR_USER_CREATED);
		}
		else
		{
			return Cvar_Get(var_name, value, 0);
		}
	}

	if(!value)
	{
		value = var->resetString;
	}

	value = Cvar_Validate(var, value, qtrue);

	if((var->flags & CVAR_LATCH) && var->latchedString)
	{
		if(!strcmp(value, var->string))
		{
			Z_Free(var->latchedString);
			var->latchedString = NULL;
			return var;
		}

		if(!strcmp(value, var->latchedString))
			return var;
	}
	else if(!strcmp(value, var->string))
	{
		return var;
	}
	// note what types of cvars have been modified (userinfo, archive, serverinfo, systeminfo)
	cvar_modifiedFlags |= var->flags;

	if(!force)
	{
		if(var->flags & CVAR_ROM)
		{
			Com_Printf("%s is read only.\n", var_name);
			return var;
		}

		if(var->flags & CVAR_INIT)
		{
			Com_Printf("%s is write protected.\n", var_name);
			return var;
		}

		if(var->flags & CVAR_LATCH)
		{
			if(var->latchedString)
			{
				if(strcmp(value, var->latchedString) == 0)
					return var;
				Z_Free(var->latchedString);
			}
			else
			{
				if(strcmp(value, var->string) == 0)
					return var;
			}

			Com_Printf("%s will be changed upon restarting.\n", var_name);
			var->latchedString = CopyString(value);
			var->modified = qtrue;
			var->modificationCount++;
			return var;
		}

		if((var->flags & CVAR_CHEAT) && !cvar_cheats->integer)
		{
			Com_Printf("%s is cheat protected.\n", var_name);
			return var;
		}

	}
	else
	{
		if(var->latchedString)
		{
			Z_Free(var->latchedString);
			var->latchedString = NULL;
		}
	}

	if(!strcmp(value, var->string))
		return var;				// not changed

	var->modified = qtrue;
	var->modificationCount++;

	Z_Free(var->string);		// free the old value string

	var->string = CopyString(value);
	var->value = atof(var->string);
	var->integer = atoi(var->string);

	return var;
}

/*
============
Cvar_Set
============
*/
void Cvar_Set(const char *var_name, const char *value)
{
	Cvar_Set2(var_name, value, qtrue);
}

/*
============
Cvar_SetLatched
============
*/
void Cvar_SetLatched(const char *var_name, const char *value)
{
	Cvar_Set2(var_name, value, qfalse);
}

/*
============
Cvar_SetValue
============
*/
void Cvar_SetValue(const char *var_name, float value)
{
	char            val[32];

	if(value == (int)value)
	{
		Com_sprintf(val, sizeof(val), "%i", (int)value);
	}
	else
	{
		Com_sprintf(val, sizeof(val), "%f", value);
	}
	Cvar_Set(var_name, val);
}


/*
============
Cvar_Reset
============
*/
void Cvar_Reset(const char *var_name)
{
	Cvar_Set2(var_name, NULL, qfalse);
}

/*
============
Cvar_ForceReset
============
*/
void Cvar_ForceReset(const char *var_name)
{
	Cvar_Set2(var_name, NULL, qtrue);
}

/*
============
Cvar_SetCheatState

Any testing variables will be reset to the safe values
============
*/
void Cvar_SetCheatState(void)
{
	cvar_t         *var;

	// set all default vars to the safe value
	for(var = cvar_vars; var; var = var->next)
	{
		if(var->flags & CVAR_CHEAT)
		{
			// the CVAR_LATCHED|CVAR_CHEAT vars might escape the reset here 
			// because of a different var->latchedString
			if(var->latchedString)
			{
				Z_Free(var->latchedString);
				var->latchedString = NULL;
			}
			if(strcmp(var->resetString, var->string))
			{
				Cvar_Set(var->name, var->resetString);
			}
		}
	}
}

/*
============
Cvar_Command

Handles variable inspection and changing from the console
============
*/
qboolean Cvar_Command(void)
{
	cvar_t         *v;

	// check variables
	v = Cvar_FindVar(Cmd_Argv(0));
	if(!v)
	{
		return qfalse;
	}

	// perform a variable print or set
	if(Cmd_Argc() == 1)
	{
		Cvar_Print(v);
		return qtrue;
	}

	// set the value if forcing isn't required
	Cvar_Set2(v->name, Cmd_Argv(1), qfalse);
	return qtrue;
}


/*
============
Cvar_Print_f

Prints the contents of a cvar 
(preferred over Cvar_Command where cvar names and commands conflict)
============
*/
void Cvar_Print_f(void)
{
	char           *name;
	cvar_t         *cv;

	if(Cmd_Argc() != 2)
	{
		Com_Printf("usage: print <variable>\n");
		return;
	}

	name = Cmd_Argv(1);

	cv = Cvar_FindVar(name);

	if(cv)
		Cvar_Print(cv);
	else
		Com_Printf("Cvar %s does not exist.\n", name);
}

/*
============
Cvar_Toggle_f

Toggles a cvar for easy single key binding, optionally through a list of
given values
============
*/
void Cvar_Toggle_f(void)
{
	int             i, c = Cmd_Argc();
	char           *curval;

	if(c < 2)
	{
		Com_Printf("usage: toggle <variable> [value1, value2, ...]\n");
		return;
	}

	if(c == 2)
	{
		Cvar_Set2(Cmd_Argv(1), va("%d", !Cvar_VariableValue(Cmd_Argv(1))), qfalse);
		return;
	}

	if(c == 3)
	{
		Com_Printf("toggle: nothing to toggle to\n");
		return;
	}

	curval = Cvar_VariableString(Cmd_Argv(1));

	// don't bother checking the last arg for a match since the desired
	// behaviour is the same as no match (set to the first argument)
	for(i = 2; i + 1 < c; i++)
	{
		if(strcmp(curval, Cmd_Argv(i)) == 0)
		{
			Cvar_Set2(Cmd_Argv(1), Cmd_Argv(i + 1), qfalse);
			return;
		}
	}

	// fallback
	Cvar_Set2(Cmd_Argv(1), Cmd_Argv(2), qfalse);
}

/*
============
Cvar_Set_f

Allows setting and defining of arbitrary cvars from console, even if they
weren't declared in C code.
============
*/
void Cvar_Set_f(void)
{
	int             c;
	char           *cmd;
	cvar_t         *v;

	c = Cmd_Argc();
	cmd = Cmd_Argv(0);

	if(c < 2)
	{
		Com_Printf("usage: %s <variable> <value>\n", cmd);
		return;
	}
	if(c == 2)
	{
		Cvar_Print_f();
		return;
	}

	v = Cvar_Set2(Cmd_Argv(1), Cmd_ArgsFrom(2), qfalse);
	if(!v)
	{
		return;
	}
	switch (cmd[3])
	{
		case 'a':
			if(!(v->flags & CVAR_ARCHIVE))
			{
				v->flags |= CVAR_ARCHIVE;
				cvar_modifiedFlags |= CVAR_ARCHIVE;
			}
			break;
		case 'u':
			if(!(v->flags & CVAR_USERINFO))
			{
				v->flags |= CVAR_USERINFO;
				cvar_modifiedFlags |= CVAR_USERINFO;
			}
			break;
		case 's':
			if(!(v->flags & CVAR_SERVERINFO))
			{
				v->flags |= CVAR_SERVERINFO;
				cvar_modifiedFlags |= CVAR_SERVERINFO;
			}
			break;
	}
}

/*
============
Cvar_Reset_f
============
*/
void Cvar_Reset_f(void)
{
	if(Cmd_Argc() != 2)
	{
		Com_Printf("usage: reset <variable>\n");
		return;
	}
	Cvar_Reset(Cmd_Argv(1));
}

/*
============
Cvar_WriteVariables

Appends lines containing "set variable value" for all variables
with the archive flag set to qtrue.
============
*/
void Cvar_WriteVariables(fileHandle_t f)
{
	cvar_t         *var;
	char            buffer[1024];

	for(var = cvar_vars; var; var = var->next)
	{
		if(!var->name || Q_stricmp(var->name, "cl_cdkey") == 0)
			continue;

		if(var->flags & CVAR_ARCHIVE)
		{
			// write the latched value, even if it hasn't taken effect yet
			if(var->latchedString)
			{
				if(strlen(var->name) + strlen(var->latchedString) + 10 > sizeof(buffer))
				{
					Com_Printf(S_COLOR_YELLOW "WARNING: value of variable " "\"%s\" too long to write to file\n", var->name);
					continue;
				}
				Com_sprintf(buffer, sizeof(buffer), "seta %s \"%s\"\n", var->name, var->latchedString);
			}
			else
			{
				if(strlen(var->name) + strlen(var->string) + 10 > sizeof(buffer))
				{
					Com_Printf(S_COLOR_YELLOW "WARNING: value of variable " "\"%s\" too long to write to file\n", var->name);
					continue;
				}
				Com_sprintf(buffer, sizeof(buffer), "seta %s \"%s\"\n", var->name, var->string);
			}
			FS_Write(buffer, strlen(buffer), f);
		}
	}
}

/*
============
Cvar_List_f
============
*/
void Cvar_List_f(void)
{
	cvar_t         *var;
	int             i;
	char           *match;

	if(Cmd_Argc() > 1)
	{
		match = Cmd_Argv(1);
	}
	else
	{
		match = NULL;
	}

	i = 0;
	for(var = cvar_vars; var; var = var->next, i++)
	{
		if(!var->name || (match && !Com_Filter(match, var->name, qfalse)))
			continue;

		if(var->flags & CVAR_SERVERINFO)
		{
			Com_Printf("S");
		}
		else
		{
			Com_Printf(" ");
		}
		if(var->flags & CVAR_SYSTEMINFO)
		{
			Com_Printf("s");
		}
		else
		{
			Com_Printf(" ");
		}
		if(var->flags & CVAR_USERINFO)
		{
			Com_Printf("U");
		}
		else
		{
			Com_Printf(" ");
		}
		if(var->flags & CVAR_ROM)
		{
			Com_Printf("R");
		}
		else
		{
			Com_Printf(" ");
		}
		if(var->flags & CVAR_INIT)
		{
			Com_Printf("I");
		}
		else
		{
			Com_Printf(" ");
		}
		if(var->flags & CVAR_ARCHIVE)
		{
			Com_Printf("A");
		}
		else
		{
			Com_Printf(" ");
		}
		if(var->flags & CVAR_LATCH)
		{
			Com_Printf("L");
		}
		else
		{
			Com_Printf(" ");
		}
		if(var->flags & CVAR_CHEAT)
		{
			Com_Printf("C");
		}
		else
		{
			Com_Printf(" ");
		}
		if(var->flags & CVAR_USER_CREATED)
		{
			Com_Printf("?");
		}
		else
		{
			Com_Printf(" ");
		}

		Com_Printf(" %s \"%s\"\n", var->name, var->string);
	}

	Com_Printf("\n%i total cvars\n", i);
	Com_Printf("%i cvar indexes\n", cvar_numIndexes);
}

/*
============
Cvar_Unset

Unsets a cvar
============
*/

cvar_t         *Cvar_Unset(cvar_t * cv)
{
	cvar_t         *next = cv->next;

	if(cv->name)
		Z_Free(cv->name);
	if(cv->string)
		Z_Free(cv->string);
	if(cv->latchedString)
		Z_Free(cv->latchedString);
	if(cv->resetString)
		Z_Free(cv->resetString);

	if(cv->prev)
		cv->prev->next = cv->next;
	else
		cvar_vars = cv->next;
	if(cv->next)
		cv->next->prev = cv->prev;

	if(cv->hashPrev)
		cv->hashPrev->hashNext = cv->hashNext;
	else
		hashTable[cv->hashIndex] = cv->hashNext;
	if(cv->hashNext)
		cv->hashNext->hashPrev = cv->hashPrev;

	Com_Memset(cv, '\0', sizeof(*cv));

	return next;
}

/*
============
Cvar_Unset_f

Unsets a userdefined cvar
============
*/

void Cvar_Unset_f(void)
{
	cvar_t         *cv;

	if(Cmd_Argc() != 2)
	{
		Com_Printf("Usage: %s <varname>\n", Cmd_Argv(0));
		return;
	}

	cv = Cvar_FindVar(Cmd_Argv(1));

	if(!cv)
		return;

	if(cv->flags & CVAR_USER_CREATED)
		Cvar_Unset(cv);
	else
		Com_Printf("Error: %s: Variable %s is not user created.\n", Cmd_Argv(0), cv->name);
}



/*
============
Cvar_Restart

Resets all cvars to their hardcoded values and removes userdefined variables
and variables added via the VMs if requested.
============
*/

void Cvar_Restart(qboolean unsetVM)
{
	cvar_t         *curvar;

	curvar = cvar_vars;

	while(curvar)
	{
		if((curvar->flags & CVAR_USER_CREATED) || (unsetVM && (curvar->flags & CVAR_VM_CREATED)))
		{
			// throw out any variables the user/vm created
			curvar = Cvar_Unset(curvar);
			continue;
		}

		if(!(curvar->flags & (CVAR_ROM | CVAR_INIT | CVAR_NORESTART)))
		{
			// Just reset the rest to their default values.
			Cvar_Set2(curvar->name, curvar->resetString, qfalse);
		}

		curvar = curvar->next;
	}
}


/*
============
Cvar_Restart_f

Resets all cvars to their hardcoded values
============
*/
void Cvar_Restart_f(void)
{
	Cvar_Restart(qfalse);
}

/*
=====================
Cvar_InfoString
=====================
*/
char           *Cvar_InfoString(int bit)
{
	static char     info[MAX_INFO_STRING];
	cvar_t         *var;

	info[0] = 0;

	for(var = cvar_vars; var; var = var->next)
	{
		if(var->name && (var->flags & bit))
			Info_SetValueForKey(info, var->name, var->string);
	}

	return info;
}

/*
=====================
Cvar_InfoString_Big

  handles large info strings ( CS_SYSTEMINFO )
=====================
*/
char           *Cvar_InfoString_Big(int bit)
{
	static char     info[BIG_INFO_STRING];
	cvar_t         *var;

	info[0] = 0;

	for(var = cvar_vars; var; var = var->next)
	{
		if(var->name && (var->flags & bit))
			Info_SetValueForKey_Big(info, var->name, var->string);
	}
	return info;
}



/*
=====================
Cvar_InfoStringBuffer
=====================
*/
void Cvar_InfoStringBuffer(int bit, char *buff, int buffsize)
{
	Q_strncpyz(buff, Cvar_InfoString(bit), buffsize);
}

/*
=====================
Cvar_CheckRange
=====================
*/
void Cvar_CheckRange(cvar_t * var, float min, float max, qboolean integral)
{
	var->validate = qtrue;
	var->min = min;
	var->max = max;
	var->integral = integral;

	// Force an initial range check
	Cvar_Set(var->name, var->string);
}

/*
=====================
Cvar_Register

basically a slightly modified Cvar_Get for the interpreted modules
=====================
*/
void Cvar_Register(vmCvar_t * vmCvar, const char *varName, const char *defaultValue, int flags)
{
	cvar_t         *cv;

	// There is code in Cvar_Get to prevent CVAR_ROM cvars being changed by the
	// user. In other words CVAR_ARCHIVE and CVAR_ROM are mutually exclusive
	// flags. Unfortunately some historical game code (including single player
	// baseq3) sets both flags. We unset CVAR_ROM for such cvars.
	if((flags & (CVAR_ARCHIVE | CVAR_ROM)) == (CVAR_ARCHIVE | CVAR_ROM))
	{
		Com_DPrintf(S_COLOR_YELLOW "WARNING: Unsetting CVAR_ROM cvar '%s', " "since it is also CVAR_ARCHIVE\n", varName);
		flags &= ~CVAR_ROM;
	}

	cv = Cvar_Get(varName, defaultValue, flags | CVAR_VM_CREATED);

	if(!vmCvar)
	{
		return;
	}
	vmCvar->handle = cv - cvar_indexes;
	vmCvar->modificationCount = -1;
	Cvar_Update(vmCvar);
}


/*
=====================
Cvar_Update

updates an interpreted modules' version of a cvar
=====================
*/
void Cvar_Update(vmCvar_t * vmCvar)
{
	cvar_t         *cv = NULL;

	assert(vmCvar);

	if((unsigned)vmCvar->handle >= cvar_numIndexes)
	{
		Com_Error(ERR_DROP, "Cvar_Update: handle out of range");
	}

	cv = cvar_indexes + vmCvar->handle;

	if(cv->modificationCount == vmCvar->modificationCount)
	{
		return;
	}
	if(!cv->string)
	{
		return;					// variable might have been cleared by a cvar_restart
	}
	vmCvar->modificationCount = cv->modificationCount;
	if(strlen(cv->string) + 1 > MAX_CVAR_VALUE_STRING)
		Com_Error(ERR_DROP, "Cvar_Update: src %s length %zd exceeds MAX_CVAR_VALUE_STRING", cv->string, strlen(cv->string));
	Q_strncpyz(vmCvar->string, cv->string, MAX_CVAR_VALUE_STRING);

	vmCvar->value = cv->value;
	vmCvar->integer = cv->integer;
}

/*
==================
Cvar_CompleteCvarName
==================
*/
void Cvar_CompleteCvarName(char *args, int argNum)
{
	if(argNum == 2)
	{
		// Skip "<cmd> "
		char           *p = Com_SkipTokens(args, 1, " ");

		if(p > args)
			Field_CompleteCommand(p, qfalse, qtrue);
	}
}

/*
============
Cvar_Init

Reads in all archived cvars
============
*/
void Cvar_Init(void)
{
	Com_Memset(cvar_indexes, '\0', sizeof(cvar_indexes));
	Com_Memset(hashTable, '\0', sizeof(hashTable));

	cvar_cheats = Cvar_Get("sv_cheats", "1", CVAR_ROM | CVAR_SYSTEMINFO);

	Cmd_AddCommand("print", Cvar_Print_f);
	Cmd_AddCommand("toggle", Cvar_Toggle_f);
	Cmd_SetCommandCompletionFunc("toggle", Cvar_CompleteCvarName);
	Cmd_AddCommand("set", Cvar_Set_f);
	Cmd_SetCommandCompletionFunc("set", Cvar_CompleteCvarName);
	Cmd_AddCommand("sets", Cvar_Set_f);
	Cmd_SetCommandCompletionFunc("sets", Cvar_CompleteCvarName);
	Cmd_AddCommand("setu", Cvar_Set_f);
	Cmd_SetCommandCompletionFunc("setu", Cvar_CompleteCvarName);
	Cmd_AddCommand("seta", Cvar_Set_f);
	Cmd_SetCommandCompletionFunc("seta", Cvar_CompleteCvarName);
	Cmd_AddCommand("reset", Cvar_Reset_f);
	Cmd_SetCommandCompletionFunc("reset", Cvar_CompleteCvarName);
	Cmd_AddCommand("unset", Cvar_Unset_f);
	Cmd_SetCommandCompletionFunc("unset", Cvar_CompleteCvarName);

	Cmd_AddCommand("cvarlist", Cvar_List_f);
	Cmd_AddCommand("cvar_restart", Cvar_Restart_f);
}
